package com.wlf.server.common.config;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaHttpMethod;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.wlf.server.common.service.SysMenuService;
import org.slf4j.Logger;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

/**
 * springMVC配置类
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    private static final Logger log = org.slf4j.LoggerFactory.getLogger(WebMvcConfig.class);
    @Resource
    SysMenuService menuService;
    @Resource
    FileConfig.LocalFileConfig localFileConfig;

    /**
     * 配置本地文件夹映射
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler(localFileConfig.getWebPath() + "/**")
                .addResourceLocations("file:" + localFileConfig.getAttachmentPath() + "/");
        WebMvcConfigurer.super.addResourceHandlers(registry);
    }

    // 注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册Sa-Token的路由拦截器
        registry.addInterceptor(new SaInterceptor((o) -> {
            // 这里也是跨域需要写的代码，如果配置了反相代理，建议把这行去掉增加系统接口的安全性。
            SaRouter.match(SaHttpMethod.OPTIONS)
                    .free(r -> log.info("--------OPTIONS预检请求，不做处理"))
                    .stop();
            System.out.println("-------- 前端访问path：" + SaHolder.getRequest().getRequestPath());
            // 登录认证 -- 拦截所有路由，并排除/main/login 用于开放登录
            SaRouter.match("/**")
                    .notMatch("/actuator/**") // 监控 测试完成后一样需要登录才能访问，不然挺危险
                    .notMatch("/captcha/**") // 验证码
                    .notMatch("/main/login") // 登陆
                    .notMatch("/main/avatar") // 获取头像
                    .notMatch("/main/logout") // 注销登陆
                    .notMatch("/error/**") // 默认错误页面
                    .notMatch("/page/**") // 静态资源
                    .notMatch(localFileConfig.getWebPath() + "/**") // 附件图片读取
                    .check(r -> StpUtil.checkLogin());
            List<SysMenuService.UrlAndPower> list = menuService.urlAndPowerList();
            for (SysMenuService.UrlAndPower i : list) {
                SaRouter.match(i.getUrl()).check(r -> StpUtil.checkPermission(i.getPower()));
            }
        }));
    }


    /**
     * 解决跨域问题，项目部署的时候可以使用nginx配置一个反向代理，就可以不需要配置这个了。
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .exposedHeaders("Access-Control-Allow-Origin")
                .allowCredentials(true);
        WebMvcConfigurer.super.addCorsMappings(registry);
    }

    /**
     * JSON，出入日期的全局处理，如果实体类上有格式注解以注解为准。
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> builder.modulesToInstall(getModel())
                // JSON中有的属性，但实体类中没有这个属性，默认true会报错，关闭false则不报错。
                .failOnUnknownProperties(true)
                // 为null的属性不进行序列化JSON的操作
                .serializationInclusion(JsonInclude.Include.NON_NULL)
                // Date格式化--yyyy-MM-dd HH:mm:ss
                .dateFormat(new SimpleDateFormat(DatePattern.NORM_DATETIME_PATTERN));
    }

    private JavaTimeModule getModel() {
        JavaTimeModule module = new JavaTimeModule();
        //localDateTime格式--yyyy-MM-dd HH:mm:ss
        module.addDeserializer(LocalDateTime.class,
                new LocalDateTimeDeserializer(DateTimeFormatter
                        .ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
        module.addSerializer(LocalDateTime.class,
                new LocalDateTimeSerializer(DateTimeFormatter
                        .ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
        // localDate格式化--yyyy-MM-dd
        module.addDeserializer(LocalDate.class,
                new LocalDateDeserializer(DateTimeFormatter
                        .ofPattern(DatePattern.NORM_DATE_PATTERN)));
        module.addSerializer(LocalDate.class,
                new LocalDateSerializer(DateTimeFormatter
                        .ofPattern(DatePattern.NORM_DATE_PATTERN)));
        // localTime格式化--HH:mm:ss
        module.addDeserializer(LocalTime.class,
                new LocalTimeDeserializer(DateTimeFormatter
                        .ofPattern(DatePattern.NORM_TIME_PATTERN)));
        module.addSerializer(LocalTime.class,
                new LocalTimeSerializer(DateTimeFormatter
                        .ofPattern(DatePattern.NORM_TIME_PATTERN)));
        return module;
    }
}

